# -*- coding: utf-8 -*-
from django import forms
from django.db.models import Q
from django.contrib.auth.models import User, Group
from skoolmaster.syllabus.models import UniversityProgram, Subject, FacultyModule, FacultyYear, SchoolYear
from skoolmaster.general.models import UserProfile, Enrollment
from skoolmaster.general.widgets import HorizontalFilterWidget, CustomDateWidget
import skoolmaster.general.search as search

class ReportEnrolledForm(forms.Form):
  """
  A form for selecting enrolled students criteria for report generation.
  """
  school_year = forms.ModelChoiceField(
    label = u"Šolsko leto",
    queryset = SchoolYear.objects.all().order_by("-name"),
    empty_label = u"ne glede",
    required = False
  )
  faculty_year = forms.ModelChoiceField(
    label = u"Letnik",
    queryset = FacultyYear.objects.all(),
    empty_label = u"ne glede",
    required = False
  )
  university_program = forms.ModelChoiceField(
    label = u"Študijski program",
    queryset = UniversityProgram.objects.all(),
    empty_label = u"ne glede",
    required = False
  )
  module = forms.ModelChoiceField(
    label = u"Modul",
    queryset = FacultyModule.objects.filter(required = False),
    empty_label = u"ne glede",
    required = False
  )
  enroll_type = forms.ChoiceField(
    label = u"Vrsta vpisa",
    choices = (('0', u"ne glede"),) + Enrollment.ENROLL_TYPE,
    required = False
  )
  study_mode = forms.ChoiceField(
    label = u"Način študija",
    choices = (('0', u"ne glede"),) + Enrollment.STUDY_MODES,
    required = False
  )

class ReportSubjectForm(forms.Form):
  """
  A form for selecting what subject and which school years to generate
  the report for.
  """
  def __init__(self, user, *args, **kwargs):
    """
    Class constructor.
    """
    super(ReportSubjectForm, self).__init__(*args, **kwargs)
    
    # A field for selecting multiple school years
    self.fields['school_years'] = forms.ModelMultipleChoiceField( 
      label = u"Šolska leta",
      queryset = SchoolYear.objects.all().order_by("-name"),
      widget = HorizontalFilterWidget('school_years', title = u"šolska leta")
    )
    self.fields['school_years'].top_label = True
    
    # A field for selecting a subject (only display subjects that are
    # selective - this means that a direct relation to FaculityYear exists)
    subject_queryset = Subject.objects.filter(faculty_year__isnull = False)
    if not user.get_profile().is_administrator():
      subject_queryset = subject_queryset.filter(lecturers = user)
    
    self.fields['subject'] = forms.ModelChoiceField(
      label = u"Predmet",
      queryset = subject_queryset.order_by("name"),
      empty_label = None
    )
  
class UniversityProgramForm(forms.Form):
  """
  Custom UniversityProgram form.
  """
  model = UniversityProgram
  def __init__(self, *args, **kwargs):
    self.edit = kwargs.pop('edit', False)
    instance = kwargs.pop('instance', None)
    super(UniversityProgramForm, self).__init__(*args, **kwargs)
    self.fields['id'] = forms.RegexField(regex = '^[0-9a-zA-Z]{4}$', label = u"Šifra", initial = instance.id if instance else None, error_messages = { 'invalid' : u"Šifra študijskega programa mora biti sestavljena iz 4 števk." })
    self.fields['name'] = forms.CharField(label = u"Študijski program", initial = instance.name if instance else None)
    self.fields['disabled'] = forms.BooleanField(label = u"Šifra ni več veljavna", initial = instance.disabled if instance else None, required = False)
    
    if self.edit:
      self.fields['id'].widget.attrs.update({'readonly' : 'readonly'})    
  
  def clean_id(self):
    id = self.cleaned_data['id'].upper()
    try:
        UniversityProgram.objects.get(id = id)
    except:
        return id
      
    if not self.edit:
      raise forms.ValidationError('Študijski program s to šifro že obstaja!')
    else:
      return id
  
  def save(self):
    UniversityProgram(
      id = self.cleaned_data['id'].upper(),
      name = self.cleaned_data['name'].upper(),
      disabled = self.cleaned_data['disabled']
    ).save()

  @staticmethod
  def search_logic(query, queryset):
    return search.id_or_name_search_logic(query, queryset)

class SubjectForm(forms.Form):
  """
  Custom Subject form.
  """
  model = Subject
  def __init__(self, *args, **kwargs):
    self.edit = kwargs.pop('edit', False)
    instance = kwargs.pop('instance', None)
    super(SubjectForm, self).__init__(*args, **kwargs)
    self.fields['id'] = forms.RegexField(regex = '^[0-9a-zA-Z]{5}$', label = u"Šifra", initial = instance.id if instance else None, error_messages = { 'invalid' : u"Šifra predmeta mora biti sestavljena iz 5 števk." })
    self.fields['name'] = forms.CharField(label = u"Predmet", initial = instance.name if instance else None)
    self.fields['ects'] = forms.IntegerField(label = u"ECTS", initial = instance.ects if instance else None)
    self.fields['grade_mode'] = forms.ChoiceField(label = u"Vrsta ocene", initial = instance.grade_mode if instance else None, choices = Subject.GRADE_MODE_CHOICES)
    self.fields['disabled'] = forms.BooleanField(label = u"Šifra ni več veljavna", initial = instance.disabled if instance else None, required = False)
    
    if self.edit:
      self.fields['id'].widget.attrs.update({'readonly' : 'readonly'})    

  def clean_id(self):
    id = self.cleaned_data['id'].upper()
    try:
        Subject.objects.get(id = id)
    except:
        return id
    if not self.edit:
      raise forms.ValidationError('Predmet s to šifro že obstaja!')
    else:
      return id

  def save(self):
    Subject(
      id = self.cleaned_data['id'].upper(),
      name = self.cleaned_data['name'].upper(),
      ects = self.cleaned_data['ects'],
      grade_mode = self.cleaned_data['grade_mode'],
      disabled = self.cleaned_data['disabled']
    ).save()

  @staticmethod
  def search_logic(query, queryset):
    return search.id_or_name_search_logic(query, queryset)

class LecturersForm(forms.Form):
  """
  Custom lecturers edit form.
  """
  def __init__(self, subject, *args, **kwargs):
    super(LecturersForm, self).__init__(*args, **kwargs)
    # This is needed for the save() method
    self.instance = subject
    # Only select the users which are lecturers
    lecturers = User.objects.filter(groups = Group.objects.get(pk = 2), userprofile__disabled = False).order_by('userprofile__surname')
    # Define the field with the custom horizontal filter widget
    self.fields['current_lecturers'] = forms.MultipleChoiceField(
      initial = [lecturer.pk for lecturer in subject.current_lecturers.all()], 
      choices = [(lecturer.pk, str(lecturer.get_profile())) for lecturer in lecturers],
      widget = HorizontalFilterWidget('current_lecturers'),
      error_messages = {'required' : 'Predmet potrebuje vsaj 1 izvajalca!'}
    )
    self.fields['current_lecturers'].label = None
    
  def clean_current_lecturers(self):
    """
    Additional validation of the current_lecturers field.
    """
    lecturers = self.cleaned_data['current_lecturers']
    if len(lecturers) > 3:
      raise forms.ValidationError(u"Največje število izvajalcev je 3!")
    
    for id in lecturers:
      lecturer = UserProfile.objects.get(user = id)
      if lecturer.disabled:
        raise forms.ValidationError(u"Izvajalec %s, ni (več) veljaven!" % lecturer)
      
    return lecturers
  
  def save(self):
    self.instance.current_lecturers.clear()
    self.instance.current_lecturers.add(*self.cleaned_data['current_lecturers'])
    self.instance.lecturers.add(*self.instance.current_lecturers.all())
    self.instance.save()
    return self.instance
    

class FacultyYearForm(forms.ModelForm):
  class Meta:
    model = FacultyYear
    fields = ('name',)
    
  @staticmethod
  def search_logic(query, queryset):
    return search.id_or_name_search_logic(query, queryset)
  
  def save(self):
    """
    Saves the posted data - needed to force upper-case strings.
    """
    entry = forms.ModelForm.save(self)
    entry.name = entry.name.upper()
    entry.save()
    return entry

class FacultyModuleForm(forms.ModelForm):
  class Meta:
    model = FacultyModule
    exclude = ('subjects',)
    
  def save(self):
    """
    Saves the posted data - needed to force upper-case strings.
    """
    entry = forms.ModelForm.save(self)
    entry.name = entry.name.upper()
    entry.save()
    return entry

class SelectSubjectForm(forms.Form):
  """
  Class for producing forms for subject selection.
  
  Based on the given information the form displays
  the appropriate fields.
  """
  program = forms.ModelChoiceField(
    UniversityProgram.objects.all().order_by("name"),
    label = u'Studijski program'
  )
  
  def __init__(self, prog_id=None, year_id=None, module_id=None, subject_id=None, *args, **kwargs):
    super(SelectSubjectForm, self).__init__(*args, **kwargs)
    
    # Provide available modules based on the selected program
    if prog_id:
      selected_prog = UniversityProgram.objects.get(id=prog_id)
      self.fields['program'].initial = getattr(selected_prog, 'id', None)
      self.fields['year'] = forms.ModelChoiceField(
        selected_prog.faculty_years.all().order_by("name"),
        label = u'Letnik'
      )
    else:
      return
    
    # Provide available modules based on the selected program
    if year_id:
      # In case of selection changes
      try:
        selected_year = selected_prog.faculty_years.get(id=year_id)
      except:
        return
      self.fields['year'].initial = getattr(selected_year, 'id', None)
      self.fields['module'] = forms.ModelChoiceField(
        selected_year.modules.all().order_by("name"),
        label = u'Modul'
      )
    else:
      return
    
    # Provide available subject based on the selected module
    if module_id:
      # In case of selection changes
      try:
        selected_module = selected_year.modules.get(id=module_id)
      except:
        return
      self.fields['module'].initial = getattr(selected_module, 'id', None)
      self.fields['subject'] = forms.ModelChoiceField(
        selected_module.subjects.all().order_by("name"),
        label = u'Predmet'
      )
      # To be usable by subclasses
      self.selected_module = selected_module
    else:
      return

class SchoolYearForm(forms.Form):
  """
  Form for creating a school year.
  """  
  def __init__(self, *args, **kwargs):
    instance = kwargs.pop('instance', None)
    self.instance = instance
    self.edit = kwargs.pop('edit', False)
    super(SchoolYearForm, self).__init__(*args, **kwargs)
    if not self.edit:
      # Make suggestion; get latest year and increment the years
      try:
        instance = SchoolYear.objects.latest()
        year_one, year_two = map(lambda y: int(y), instance.name.split('/'))
        instance.name = str(year_one + 1) + '/' + str(year_two + 1)
        instance.start = instance.end = None
      except:
        instance = None
    
    self.fields['year'] = forms.RegexField(label = 'Šolsko leto', regex = '^2[0-9]{3}/2[0-9]{3}$', initial = instance.name if instance else None, error_messages = {'invalid' : u'Naziv leta mora biti oblike LETO/LETO.'})
    self.fields['date_start'] = forms.DateField(label = u"Datum začetka študijskega leta", initial = instance.start if instance else None, widget = CustomDateWidget())
    self.fields['date_end']   = forms.DateField(label = u"Datum konca študijskega leta", initial = instance.end if instance else None, widget = CustomDateWidget())
    
  def clean_year(self):
    year = self.cleaned_data['year']
    year_one, year_two = map(lambda y: int(y), year.split('/'))
    if year_two - 1 != year_one:
      raise forms.ValidationError('Nepravilna oblika imena šolskega leta.')
    try:
      dupe = SchoolYear.objects.get(name = year)
      if self.edit and dupe.pk == self.instance.pk:
        return year
    except:
      return year
    raise forms.ValidationError('To šolsko leto že obstaja.')
  
  def clean_date_end(self):
    if self.cleaned_data['date_start'] >= self.cleaned_data['date_end']:
      raise forms.ValidationError('Šolske leto se ne more končati preden se začne.')
    return self.cleaned_data['date_end']
  
  def save(self):
    if not self.edit:
      current_year = SchoolYear.objects.latest()
      current_year.archive_year()
      year = SchoolYear(
        name = self.cleaned_data['year'],
        start = self.cleaned_data['date_start'],
        end = self.cleaned_data['date_end']
      )
      year.save()
      year.archive_year()
    else: 
      # Editing existing entry 
      year = SchoolYear.objects.get(pk = self.instance.pk)
      year.name = self.cleaned_data['year']
      year.start = self.cleaned_data['date_start']
      year.end = self.cleaned_data['date_end']
      year.save()
